/*
 * Copyright (C) 2011, Pavel Samko <bulldozerbsg@gmail.com>
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the "Pavel Samko" nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
 * OF SUCH DAMAGE.
 */

#include "VCTCPServer.h"

VCTCPServerClientThread::VCTCPServerClientThread(VCTCPServerClient & mod)
: VCThread()
, m_mod(mod)
{

}

int VCTCPServerClientThread::Execute(void * data)
{
	while (!m_mod.m_needexit)
	{
		// Send
		{
			VCAutoMutex bufflock(m_mod.m_send_buff_mutex);
			size_t data_size = m_mod.m_send_buff.Size();
			if (data_size)
			{
				VCAutoMutex socklock(m_mod.m_socket_mutex);
				size_t send_size = data_size;
				if (m_mod.m_socket != VC_SOCKET_INVALID)
				{
					if (VCNetSend(m_mod.m_socket, m_mod.m_send_buff.GetDataPtr(), &send_size))
					{
						m_mod.m_send_buff.PopSize(send_size);
					} else
					{
						TRACE("tcp server client send false")
						m_mod.m_needexit = true;
					}
				}
			}
		}
		// Recv
		{
			VCAutoMutex lock(m_mod.m_socket_mutex);
			size_t recv_size = VCTCPSERVER_BUFF_SIZE;
			if (m_mod.m_socket != VC_SOCKET_INVALID)
			{
				if (VCNetRecv(m_mod.m_socket, m_recv_buff, &recv_size))
				{
					m_mod.OnRecv(m_recv_buff, recv_size);
				} else
				{
					TRACE("tcp server client recv false")
					m_mod.m_needexit = true;
				}
			}
		}
		{
			VCAutoMutex lock(m_mod.m_socket_mutex);
			if (m_mod.m_socket == VC_SOCKET_INVALID)
			{
				TRACE("tcp server client socket invalid")
				m_mod.m_needexit = true;
			}
		}
		VCSleep(1);
	}
	return 0;
}

void VCTCPServerClientThread::OnExit(int exitcode)
{
	{
		VCAutoMutex lock(m_mod.m_socket_mutex);
		if (m_mod.m_socket != VC_SOCKET_INVALID)
		{
			VCNetClose(m_mod.m_socket);
			m_mod.m_socket = VC_SOCKET_INVALID;
		}
	}
	m_mod.OnDisconect();
	{
		VCAutoMutex lock(m_mod.m_server_thread.m_disconnectedclients_mutex);
		m_mod.m_server_thread.m_disconnectedclients.push_back(&m_mod);
	}
}

VCTCPServerClient::VCTCPServerClient(VC_SOCKET socket, VC_SOCKET_ADDR_IN * addr, VCTCPServerThread & server_thread)
: m_needexit(false)
, m_socket(socket)
, m_server_thread(server_thread)
, m_send_buff(VCTCPSERVER_BUFF_SIZE)
{
	m_thread = new VCTCPServerClientThread(*this);
}

VCTCPServerClient::~VCTCPServerClient()
{
	m_needexit = true;
	if (m_thread)
	{
		m_thread->Wait();
		delete m_thread;
	}
}

void VCTCPServerClient::Disconnect()
{
	m_needexit = true;
}

bool VCTCPServerClient::IsConnected()
{
	VCAutoMutex lock(m_socket_mutex);
	return (m_socket != VC_SOCKET_INVALID);
}

void VCTCPServerClient::Send(void * data, size_t data_size)
{
	if (data && data_size)
	{
		VCAutoMutex lock(m_send_buff_mutex);
		m_send_buff.Push(data, data_size);
	}
}

VCTCPServer & VCTCPServerClient::GetServer()
{
	return m_server_thread.m_mod;
}

void VCTCPServerClient::OnRecv(void * data, size_t data_size)
{
	// need override
}

void VCTCPServerClient::OnDisconect()
{
	// need override
}

VCTCPServerThread::VCTCPServerThread(VCTCPServer & mod)
: m_mod(mod)
{
}

int VCTCPServerThread::Execute(void * data)
{
	VC_SOCKET_ADDR_IN client_addr;
	VC_SOCKET client;
	while (!m_mod.m_needstop)
	{
		{
			VCAutoMutex lock_server(m_mod.m_socket_mutex);
			if (m_mod.m_socket != VC_SOCKET_INVALID)
			{
				client = VCNetAccept(m_mod.m_socket, &client_addr);
				if (client != VC_SOCKET_INVALID)
				{
					VCAutoMutex lock_client(m_clients_mutex);
					VCTCPServerClient * serverclient = m_mod.OnConnect(client, &client_addr, *this);
					if (serverclient)
					{
						m_clients.push_back(serverclient);
						TRACE("connected from[%s]", VCNetGetIp(&client_addr))
					}
				}
			}
		}
		{
			VCAutoMutex lock_disconnected(m_disconnectedclients_mutex);
			if (!m_disconnectedclients.empty())
			{
				VCAutoMutex lock_client(m_clients_mutex);
				std::list<VCTCPServerClient *>::iterator it = m_disconnectedclients.begin();
				while (it != m_disconnectedclients.end())
				{
					std::list<VCTCPServerClient *>::iterator it2 = m_clients.begin();
					while (it2 != m_clients.end())
					{
						if (*it == *it2)
						{
							m_clients.erase(it2);
							break;
						}
						it2++;
					}
					VCTCPServerClient * item = *it;
					delete item;
					it++;
				}
				m_disconnectedclients.clear();
			}
		}
		VCSleep(1);
	}
	return 0;
}

void VCTCPServerThread::OnExit(int exitcode)
{
	{
		VCAutoMutex lock(m_clients_mutex);
		std::list<VCTCPServerClient *>::iterator it = m_clients.begin();
		while (it != m_clients.end())
		{
			VCTCPServerClient * serverclient = *it;
			delete serverclient;
			it++;
		}
		m_clients.clear();
	}
	{
		VCAutoMutex lock(m_disconnectedclients_mutex);
		m_disconnectedclients.clear();
	}
}

VCTCPServer::VCTCPServer()
: m_thread(NULL)
, m_needstop(true)
, m_socket(VC_SOCKET_INVALID)
{
	VCNetInit();
}

VCTCPServer::~VCTCPServer()
{
	StopListening();
	VCNetDeinit();
}

bool VCTCPServer::Listen(const char * on_ip, unsigned short on_port)
{
	bool result = false;
	StopListening();
	{
		VCAutoMutex lock(m_socket_mutex);
		VC_SOCKET_ADDR_IN on;
		if (VCNetInitAddrIn(&on, on_ip, on_port))
		{
			m_socket = VCNetSocketTCP();
			if (m_socket != VC_SOCKET_INVALID)
			{
				if (VCNetBind(m_socket, &on) && VCNetListen(m_socket))
				{
					TRACE("tcp server start ok on port[%hu]", on_port)
					result = true;
					m_needstop = false;
					m_thread = new VCTCPServerThread(*this);
				}
				if (!result)
				{
					TRACE("tcp server start false")
					VCNetClose(m_socket);
					m_socket = VC_SOCKET_INVALID;
				}
			}
		}
	}
	return result;
}

void VCTCPServer::StopListening()
{
	m_needstop = true;
	if (m_thread)
	{
		m_thread->Wait();
		delete m_thread;
		m_thread = NULL;
	}
	if (m_socket != VC_SOCKET_INVALID)
	{
		VCAutoMutex lock(m_socket_mutex);
		VCNetClose(m_socket);
		m_socket = VC_SOCKET_INVALID;
	}
}

bool VCTCPServer::IsListening()
{
	VCAutoMutex lock(m_socket_mutex);
	return (m_socket != VC_SOCKET_INVALID);
}

VCTCPServerClient * VCTCPServer::OnConnect(VC_SOCKET socket, VC_SOCKET_ADDR_IN * addr, VCTCPServerThread & server_thread)
{
	return new VCTCPServerClient(socket, addr, server_thread);
}

